/**
 *  Project: CloudstickUtils 
 *  kr.cloudstick.util.xml-CSXml.java
 * 	
 *	Copyright 2013 Cloudstick (www.cloudstick.kr)
 *
 *	Written by htkim
 *		2013. 3. 25. first written.
 *
 *	Note:	
 */
package kr.cloudstick.util.xml;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import kr.cloudstick.util.CSException;
import lombok.Cleanup;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public final class CSXml {
	
	private CSXml() {
		throw new AssertionError();
	}
	
	public static String getValue(final File file, final String expression) {
		
		try {
			final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			final DocumentBuilder builder = factory.newDocumentBuilder();
			final Document doc = builder.parse(file);
			return getValue(getXmlString(doc), expression);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new CSException(e.getMessage(), e);
		}
	}
	
	public static String getValue(final String xml, final String expression) {
		
		try {
			final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			final DocumentBuilder builder = factory.newDocumentBuilder();
			final Document doc = builder.parse(new InputSource(new StringReader(xml)));
			
			final XPathFactory xPathFactory = XPathFactory.newInstance();
			final XPath xpath = xPathFactory.newXPath();
			xpath.setNamespaceContext(new CSNamespaceContextResolver(doc));
			
			final XPathExpression xpathexpression = xpath.compile(expression);
			final Object result = xpathexpression.evaluate(doc, XPathConstants.NODESET);
			if ( result == null ) {
				throw new CSException("Failed to evaluate expression.");
			}
			final NodeList nodes = (NodeList)result;
			
			if ( nodes.getLength() == 0 ) {
				return null; // NOPMD by cloudstick on 13. 3. 28 5:11
			}
			return nodes.item(0).getNodeValue();
		} catch (Exception e) {
			throw new CSException(e.getMessage(), e);
		}
	}
	
	public static String getXmlString(final Document doc) {
		
		try {
			final TransformerFactory factory = TransformerFactory.newInstance();
			@Cleanup final StringWriter stringWriter = new StringWriter();
			final Transformer transformer = factory.newTransformer();
			final StreamResult result = new StreamResult(stringWriter);
			final DOMSource source = new DOMSource(doc);
			transformer.transform(source, result);
			return stringWriter.toString();
		} catch (TransformerConfigurationException e) {
			throw new CSException("Failed to make transformer.", e);
		} catch (TransformerException e) {
			throw new CSException("Failed to transform.", e);
		} catch (IOException e) {
			throw new CSException(e.getMessage(), e);
		}
	}
	
	public static boolean validateSchema(final String schemaPath, final String xml) {
		try {
			final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			final DocumentBuilder builder = factory.newDocumentBuilder();
			final Document doc = builder.parse(new InputSource(new StringReader(xml)));
			
			final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
			final Source schemaFile = new StreamSource(new File(schemaPath));
			final Schema schema = schemaFactory.newSchema(schemaFile);
			
			final Validator validator = schema.newValidator();
			try {
				validator.validate(new DOMSource(doc));
			} catch(SAXException e) {
				return false; // NOPMD by cloudstick on 13. 3. 28 5:15
			}
			return true;
		} catch(Exception e) {
			throw new CSException(e);
		}
	}
}
